/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "app_asn1parse.h"

// Get tag name for display
static const char* GetTagName(uint8_t tag) {
    switch (tag & 0xe0) {
        case BSL_ASN1_TAG_CONT:
            return "CONT";
        default: break;
    }

    switch (tag & 0x1F) {
        case BSL_ASN1_TAG_BOOLEAN: return "BOOLEAN";
        case BSL_ASN1_TAG_INTEGER: return "INTEGER";
        case BSL_ASN1_TAG_BITSTRING: return "BIT STRING";
        case BSL_ASN1_TAG_OCTETSTRING: return "OCTET STRING";
        case BSL_ASN1_TAG_NULL: return "NULL";
        case BSL_ASN1_TAG_OBJECT_ID: return "OBJECT";
        case BSL_ASN1_TAG_ENUMERATED: return "ENUMERATED";
        case BSL_ASN1_TAG_UTF8STRING: return "UTF8String";
        case BSL_ASN1_TAG_SEQUENCE: return "SEQUENCE";
        case BSL_ASN1_TAG_SET: return "SET";
        case BSL_ASN1_TAG_PRINTABLESTRING: return "PrintableString";
        case BSL_ASN1_TAG_IA5STRING: return "IA5String";
        case BSL_ASN1_TAG_UTCTIME: return "UTCTime";
        case BSL_ASN1_TAG_GENERALIZEDTIME: return "GeneralizedTime";
        default: return "UNKNOWN";
    }
}

// Simplified ASN1 tag and length decoder using public APIs
static int32_t DecodeTagLength(uint8_t **data, uint32_t *remaining, 
                              uint8_t *tag, uint32_t *length, uint32_t *headerLen) {
    if (data == NULL || *data == NULL || remaining == NULL || *remaining == 0) {
        return BSL_NULL_INPUT;
    }
    
    uint8_t *p = *data;
    uint32_t rem = *remaining;
    uint32_t hl = 0;
    
    // Get tag
    if (rem < 1) return -1;
    *tag = *p;
    p++; rem--; hl++;
    
    // Get length
    if (rem < 1) return -1;
    uint8_t len_byte = *p;
    p++; rem--; hl++;
    
    if ((len_byte & 0x80) == 0) {
        // Short form
        *length = len_byte;
    } else {
        // Long form
        uint32_t len_octets = len_byte & 0x7F;
        if (len_octets == 0 || len_octets > 4 || len_octets > rem) {
            return -1;
        }
        
        *length = 0;
        for (uint32_t i = 0; i < len_octets; i++) {
            *length = (*length << 8) | *p;
            p++; rem--; hl++;
        }
    }
    
    if (*length > rem) {
        return -1;
    }
    
    *headerLen = hl;
    *data = p;
    *remaining = rem;
    
    return 0;
}

int32_t Asn1ParseRecursive(BSL_UIO *uio, uint8_t *data, uint32_t dataLen,
                          uint32_t depth, uint32_t offset, uint32_t dlimit,
                          uint32_t enableIndent, uint32_t hexDump) {
    
    uint8_t *p = data;
    uint32_t remaining = dataLen;
    uint32_t currentOffset = offset;
    
    while (remaining > 0) {
        uint8_t tag;
        uint32_t length;
        uint32_t headerLen;
        
        // Decode tag and length
        int32_t ret = DecodeTagLength(&p, &remaining, &tag, &length, &headerLen);
        if (ret) {
            AppPrintError("Failed to decode ASN.1 tag and length\n");
            return HITLS_APP_INVALID_ARG;
        }
        
        if (length > remaining) {
            AppPrintError("ASN.1 length exceeds remaining data\n");
            return HITLS_APP_INVALID_ARG;
        }
        
        // Print basic info
        Asn1ParseInfo(uio, currentOffset, depth, enableIndent, headerLen, length, (tag & BSL_ASN1_TAG_CONSTRUCTED) ? "cons" : "prim", GetTagName(tag));
        
        // Handle content based on type
        if (tag & BSL_ASN1_TAG_CONSTRUCTED) {
            // Constructed type - recurse
            if ((tag & 0xe0) == BSL_ASN1_TAG_CONT) {
                ret = AppPrint(uio, ":cont [ %d ]", tag & 0x1f);
                if (ret != HITLS_APP_SUCCESS) {
                    return ret;
                }
            }
            AppPrint(uio, "\n");
            ret = Asn1ParseRecursive(uio, p, length, depth + 1, 
                                   currentOffset + headerLen, dlimit,
                                   enableIndent, hexDump);
            if (ret != HITLS_APP_SUCCESS) {
                return ret;
            }
        } else {
            // Primitive type - show content
            BSL_ASN1_Buffer buf = { .tag = tag, .len = length, .buff = p };
            Asn1ParseFormat(uio, &buf, NULL, dlimit, enableIndent ? (depth + 1) * 2 : 0, hexDump);
            AppPrint(uio, "\n");
        }
        
        // Move to next item
        p += length;
        remaining -= length;
        currentOffset += headerLen + length;
    }
    
    return HITLS_APP_SUCCESS;
}

int32_t Asn1ParseDump(BSL_UIO *uio, uint8_t *data, uint32_t dataLen,
                      uint32_t dlimit, uint32_t indent, uint32_t dump) {
    if (uio == NULL || data == NULL || dataLen == 0) {
        return BSL_NULL_INPUT;
    }
    
    return Asn1ParseRecursive(uio, data, dataLen, 0, 0, dlimit, indent, dump);
}
